package main

import (
	"bufio"
	"flag"
	"fmt"
	"io"
	"io/fs"
	"mime"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

func showFiledialog() (string, error) {
	var foundExe bool
	cmd1 := exec.Command("zenity", "--file-filter=appimage | *.appimage *.Appimage *.AppImage", "--file-selection")
	foundExe = filepath.IsAbs(cmd1.Path)
	data, err := cmd1.Output()
	if err == nil {
		return strings.TrimSpace(string(data)), nil
	} else if foundExe {
		return "", err
	}
	cmd1 = exec.Command("kdialog", "--getopenfilename", ".", "*.appimage *.Appimage *.AppImage")
	data, err = cmd1.Output()
	if err == nil {
		return strings.TrimSpace(string(data)), nil
	}
	return "", err
}

func main() {
	var del1 = flag.Bool("d", false, "Delete Desktop and Icon files.")
	flag.Parse()
	appname := flag.Arg(0)
	if appname == "" {
		var err error
		appname, err = showFiledialog()
		if err != nil {
			println(err.Error())
			return
			//panic(err)
		}
	}
	appPath := appname

	if !filepath.IsAbs(appname) {
		pwd1, err := os.Getwd()
		if err != nil {
			panic(err)
		}
		appPath = filepath.Join(pwd1, appname)
	}

	println(appPath)
	cmd1 := exec.Command("chmod", "a+x", appPath)
	err := cmd1.Run()
	if err != nil {
		println(err)
	}
	tmp1 := os.TempDir()
	os.Chdir(tmp1)
	cmd2 := exec.Command(appPath, "--appimage-mount")
	p0, err := cmd2.StdoutPipe()
	if err != nil {
		panic(err)
	}
	err = cmd2.Start()
	if err != nil {
		panic(err)
	}

	rd0 := bufio.NewReader(p0)
	s0, err := rd0.ReadString('\n')
	if err != nil && err.Error() != "EOF" {
		panic(err)
	}

	defer cmd2.Wait()
	defer cmd2.Process.Kill()

	var dir0 = strings.TrimSpace(s0)
	cmd3 := exec.Command("ls", dir0)
	p1, err := cmd3.StdoutPipe()
	if err != nil {
		panic(err)
	}
	err = cmd3.Start()
	if err != nil {
		panic(err)
	}
	defer cmd3.Wait()
	var out3 string
	for {
		_, err := fmt.Fscanln(p1, &out3)
		if err != nil {
			break
		}
		if strings.HasSuffix(out3, ".desktop") {
			fmt.Printf("desktop: %s\n", out3)
			break
		}

	}

	p1.Close()

	var desktopfile = filepath.Join(dir0, out3)
	fp, err := os.Open(desktopfile)
	if err != nil {
		println("DesktopFile:", desktopfile)
		panic(err)
	}
	defer fp.Close()
	br := bufio.NewReader(fp)
	var iconName string
	for {
		line1, err := br.ReadString('\n')
		if err != nil {
			panic(err)
		}
		if strings.HasPrefix(line1, "Icon=") {
			var ok bool
			iconName, ok = strings.CutPrefix(strings.TrimSpace(line1), "Icon=")
			if ok {
				break
			} else {
				println("fail cut: ", line1)
			}
		}
	}
	//println(iconName)
	iconPath := getIconPath(iconName, dir0)
	//println(iconPath)
	home1, err := os.UserHomeDir()
	if err != nil {
		panic(err)
	}

	desktopPath := filepath.Join(home1, ".local", "share", "applications", filepath.Base(desktopfile))
	os.Mkdir(filepath.Dir(desktopPath), os.ModePerm)
	iconName = getIconName(iconPath, desktopPath)
	dstIcon := filepath.Join(home1, ".local", "share", "icons", iconName)
	os.Mkdir(filepath.Dir(dstIcon), os.ModePerm)

	if *del1 {
		os.Remove(dstIcon)
		os.Remove(desktopPath)
		fmt.Printf("删除启动器：\nDesktop: %s\nIcon:%s\n", desktopPath, dstIcon)
		return
	}
	err = copyFile(iconPath, dstIcon)
	if err != nil {
		println("Icon:", iconPath)
		panic(err)
	}

	err = modifyDesktopFile(desktopfile, desktopPath, appPath, iconName)
	if err != nil {
		panic(err)
	}
	fmt.Printf("成功创建启动器：\nDesktop: %s\nIcon:%s\n", desktopPath, dstIcon)
}

func getIconName(iconPath, desktopPath string) string {
	var name string
	desktopName := filepath.Base(desktopPath)
	prefix, ok := strings.CutSuffix(strings.ToLower(desktopName), ".desktop")
	if !ok {
		return "ErrorFormat"
	}
	//prefix := strings.Split(desktopName, ".")[0]
	iconName := filepath.Base(iconPath)
	tail := filepath.Ext(iconName)
	//tail := strings.Split(iconName, ".")[1]
	name = prefix + tail
	return name
}

func modifyDesktopFile(src, dst, exe, iconName string) error {
	fp1, err := os.Open(src)
	if err != nil {
		return err
	}
	defer fp1.Close()
	fp2, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer fp2.Close()
	br := bufio.NewReader(fp1)
	for {
		line1, _, err := br.ReadLine()
		if err != nil {
			break
		}
		s1 := string(line1)
		if strings.HasPrefix(s1, "Exec=") {
			v1 := strings.SplitN(s1, " ", 2)
			tail := ""
			if len(v1) == 2 {
				tail = " " + v1[1]
			}
			if strings.Contains(exe, " ") {
				exe = fmt.Sprintf("'%s'", exe)
			}
			s1 = fmt.Sprintf("Exec=%s%s", exe, tail)
		} else if strings.HasPrefix(s1, "TryExec=") {
			continue
		} else if strings.HasPrefix(s1, "Icon=") {
			s1 = fmt.Sprintf("Icon=%s", strings.Split(iconName, ".")[0])
		}

		fp2.WriteString(s1 + "\n")
	}
	return nil
}

func copyFile(src, dst string) error {
	fp1, err := os.Open(src)
	if err != nil {
		return err
	}
	defer fp1.Close()
	fp2, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer fp2.Close()
	_, err = io.Copy(fp2, fp1)
	return err
}

func getIconPath(name, dir1 string) string {
	println("search icon:", name, dir1)
	if strings.HasPrefix(name, "/") {
		name = filepath.Base(name)
		name = strings.Split(name, ".")[0]
	}
	println("search icon:", name)
	var res string
	err := filepath.WalkDir(dir1, func(path1 string, d fs.DirEntry, err error) error {
		if d.IsDir() {
			return nil
		}
		//fmt.Println(path1)
		name1 := filepath.Base(path1)

		if strings.HasPrefix(name1, name+".") {
			ext1 := filepath.Ext(path1)
			fmt.Println(name1, ext1)
			typ := mime.TypeByExtension(ext1)
			//fmt.Println(name1, typ)
			if strings.HasPrefix(typ, "image/") {
				res = path1
				return fmt.Errorf("Success")
			}
		}
		//fmt.Println(name1)
		return nil
	})
	if err != nil {

		if err.Error() != "Success" {
			panic(err.Error())
		} else {
			fmt.Println("find icon:", res)
		}
	}

	return res
}
